package org.aspire.core;

import org.aspire.core.loop.LoopController;
import org.aspire.data.Emptyable;
import org.aspire.data.Owner;
import org.aspire.data.Renderer;
import org.aspire.graphics.DisplayView;
import org.aspire.graphics.Frame;
import org.aspire.graphics.Strip;
import org.aspire.graphics.Transparency;
import org.aspire.io.FileIO;
import org.aspire.io.ImageIO;

/******************************************************************************
 * Aspire2D uses a Game-Console-Engine architecture at its core. The Engine
 * component provides the integration of various low-level subsystems into
 * a single-working unit that performs the so-called 'dirty works' behind the
 * game.
 * <p>
 * The Engine is designed as a final class to avoid unnecessary changes in the
 * operations provided within resulting from overriding sensitive methods such
 * as the main loop which might lead to unwanted behavior or non-working code.
 * <p>
 * It also provides a singleton instance of itself accessible through the
 * <code>Engine.getInstance()</code> method. This ensures that there will be no
 * duplicate Engine running somewhere which might produce confusion from the
 * low-level subsystems.
 * 
 * @author JPACarabuena
 *****************************************************************************/
public final class Engine implements Owner, Emptyable {

	// ========================================================================
	// Constants
	// ========================================================================
	
	/**************************************************************************
	 * A singleton instance of the Engine class. This is accessible through the
	 * <code>Engine.getInstance()</code> static method.
	 *************************************************************************/
	private static final Engine SINGLETON = new Engine();

	// ========================================================================
	// Static Methods
	// ========================================================================
	
	/**************************************************************************
	 * Static method to get the singleton instance of the Engine class.
	 * 
	 * @return	the singleton instance.
	 *************************************************************************/
	protected static final Engine getInstance() {
		return Engine.SINGLETON;
	}
	
	// ========================================================================
	// Fields
	// ========================================================================

	/**************************************************************************
	 * The display view to be used in this engine. This is a sub-system
	 * integrated into the engine which provides draw graphics.
	 *************************************************************************/
	public DisplayView display;
	
	/**************************************************************************
	 * The loop controller to be used in this engine. This is a sub-system
	 * integrated into the engine which regulates FPS. 
	 *************************************************************************/
	public LoopController controller;

	/**
	 * 
	 */
	public FileIO fileIO;
	
	/**************************************************************************
	 * The renderer to be used in this engine which will render graphics and
	 * audio data to the screen.
	 *************************************************************************/
	public Renderer renderer;
	
	/**************************************************************************
	 * The game mounted to this engine. It will be processed in the main loop
	 * once the engine is up and running.
	 *************************************************************************/
	protected Game game;
	
	/**************************************************************************
	 * Whether or not the game engine process is running. By enabling this, it
	 * tells the game engine to start processing the mounted game. Once it is
	 * set to false during the course of the game process, the engine will stop
	 * and will automatically unmount the game.
	 * 
	 * Default: false
	 *************************************************************************/
	protected boolean running;
	
	/**************************************************************************
	 * Whether or not the game engine update process is paused. By enabling
	 * this, it tells the game engine to skip the update of game except the
	 * input processing.
	 * 
	 * Default: false
	 *************************************************************************/
	protected boolean paused;
	
	// ========================================================================
	// Constructors
	// ========================================================================
	
	/**************************************************************************
	 * Disable unwanted construction of the Engine class.
	 *************************************************************************/
	protected Engine() {
		// do nothing
	}
	
	// ========================================================================
	// Getters & Setters
	// ========================================================================
	
	/**************************************************************************
	 * Returns the current calculated FPS rate for this iteration.
	 * 
	 * @return	the current FPS.
	 *************************************************************************/
	public int getCurrentFPS() {
		return this.controller.getCurrentFPS();
	}
	
	// ========================================================================
	// Override Methods
	// ========================================================================
	
	@Override
	public void clear() {
		if(this.game != null) {
			this.game = null;
		}
	}
	
	@Override
	public boolean isEmpty() {
		return this.game == null;
	}
	
	// ========================================================================
	// Methods
	// ========================================================================
	
	/**************************************************************************
	 * Start the main engine process.
	 *************************************************************************/
	public void run() {
		if(!this.running) {
			this.loadEngine();
			this.processGame();
		}
	}

	/**************************************************************************
	 * Mounts the specified game component to this engine.
	 * 
	 * @param game	the game to be processed.
	 *************************************************************************/
	public void mountGame(Game game) {
		this.game = game;
		this.game.setGameEngine(this);
	}
	
	/**************************************************************************
	 * Pause the engine update processing.
	 *************************************************************************/
	public void pause() {
		this.paused = true;
	}
	
	/**************************************************************************
	 * Resume the engine update processing.
	 *************************************************************************/
	public void resume() {
		this.paused = false;
	}
	
	/**************************************************************************
	 * Stops the processing of the game. Shuts down the engine.
	 *************************************************************************/
	public void stop() {
		this.running = false;
	}
	
	/**************************************************************************
	 * Loads the necessary system and game resources.
	 *************************************************************************/
	private void loadEngine() {
		this.game.init();
	}
	
	/**************************************************************************
	 * Starts the main game loop which process the game logic define by the
	 * mounted game. This continuously performs game update and render until
	 * the engine is stopped. At the end of the processing, the game will be
	 * destroyed and automatically unmounted to this engine.
	 *************************************************************************/
	private void processGame() {
		this.running = true;
	
		this.controller.start();
		
		long elapsedTime = 0;
		
		while(this.running) {
			this.controller.tick();
			elapsedTime = this.controller.getDelta();
			
			if(!this.paused) {
				this.game.update(elapsedTime);
			}
			
			do {
				this.renderer.setGraphicsRenderer(
						this.display.getBackBuffer());
				this.game.render(this.renderer);
			} while(!this.display.flip());
			
			this.controller.sleep();
		}
	}
	
	public Frame getFrame(String path) {
		return ImageIO.getFrame(this.fileIO.getURL(path), 
				Transparency.TRANSLUCENT);
	}
	
	public Strip getStrip(String path, int col, int row) {
		return ImageIO.getStrip(this.fileIO.getURL(path), col, row,
				Transparency.TRANSLUCENT);
	}
	
	// ========================================================================
	// Inner & Anonymous Classes
	// ========================================================================
}